/*
 * Copyright (c) Hisilicon Technologies Co., Ltd. 2019-2019. All rights reserved.
 * Description: video analysis video process queue implementation
 * Author: HiMobileCam Reference Develop Team
 * Create: 2019-11-01
 */

#include "videoprocess_queue.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* End of #ifdef __cplusplus */

typedef struct {
    pthread_mutex_t mutex;
    HI_S32 frontIdx; /* front index, means (the newest node index + 1) */
    HI_S32 rearIdx;  /* rear index, means the oldest node index        */
    HI_S32 curLen;   /* current used length                            */
    HI_S32 maxLen;   /* max length                                     */
    HI_S32 nodeSize; /* size of node                                   */
    HI_VOID** node;  /* node                                           */
} VIDEOPROCESS_Queue;

HI_HANDLE VIDEOPROCESS_CreateQueue(HI_U32 nodeSize, HI_U32 maxLen)
{
    VIDEOPROCESS_Queue *queue = (VIDEOPROCESS_Queue*)malloc(sizeof(VIDEOPROCESS_Queue));
    if (!queue) {
        return 0;
    }

    queue->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
    queue->frontIdx = 0;
    queue->rearIdx = 0;
    queue->curLen = 0;
    queue->maxLen = maxLen;
    queue->nodeSize = nodeSize;
    queue->node = (HI_VOID**)malloc(sizeof(HI_VOID*) * maxLen);
    if (queue->node == HI_NULL) {
        MLOGE("malloc failed.\n");
        HI_APPCOMM_SAFE_FREE(queue);
        return 0;
    }

    HI_S32 i;
    for (i = 0; i < queue->maxLen; i++) {
        queue->node[i] = (HI_VOID*)malloc(nodeSize);
        if (queue->node[i] == HI_NULL) {
            MLOGE("malloc failed.\n");
            break;
        }
    }

    if (i != queue->maxLen) {
        for (; i > 0; i--) {
            HI_APPCOMM_SAFE_FREE(queue->node[i-1]);
        }
        HI_APPCOMM_SAFE_FREE(queue->node);
        HI_APPCOMM_SAFE_FREE(queue);
        return 0;
    }

    return (HI_HANDLE)queue;
}

HI_VOID VIDEOPROCESS_DestroyQueue(HI_HANDLE queueHdl, HI_VOID* node, HI_U32* bufferCnt)
{
    if (queueHdl == 0) {
        MLOGE("queueHdl err\n");
        return;
    }

    VIDEOPROCESS_Queue *queue = (VIDEOPROCESS_Queue*)queueHdl;
    HI_MUTEX_LOCK(queue->mutex);
    HI_S32 i;
    HI_U32 tmpBufferCnt = 0;

    if (queue->curLen != 0) {
        HI_S32 newestIndex = queue->frontIdx;
        for (i = 0; i < queue->curLen; i++) {
            if (newestIndex > 0) {
                newestIndex = newestIndex - 1;
            } else if (newestIndex == 0) {
                newestIndex = queue->maxLen - 1;
            }

            memcpy((HI_VOID*)((HI_CHAR*)node + i * queue->nodeSize), queue->node[newestIndex], queue->nodeSize);
            tmpBufferCnt++;
        }
    }

    *bufferCnt = tmpBufferCnt;
    for (i = 0; i < queue->maxLen; i++) {
        HI_APPCOMM_SAFE_FREE(queue->node[i]);
    }

    HI_APPCOMM_SAFE_FREE(queue->node);
    HI_MUTEX_UNLOCK(queue->mutex);
    HI_MUTEX_DESTROY(queue->mutex);
    HI_APPCOMM_SAFE_FREE(queue);
}

HI_VOID VIDEOPROCESS_ClearQueue(HI_HANDLE queueHdl, HI_VOID* node, HI_U32* bufferCnt)
{
    if (queueHdl == 0) {
        MLOGE("queueHdl err\n");
        return;
    }

    VIDEOPROCESS_Queue *queue = (VIDEOPROCESS_Queue*)queueHdl;
    HI_MUTEX_LOCK(queue->mutex);
    HI_U32 tmpBufferCnt = 0;

    if (queue->curLen != 0) {
        HI_S32 newestIndex = queue->frontIdx;
        for (HI_S32 i = 0; i < queue->curLen; i++) {
            if (newestIndex > 0) {
                newestIndex = newestIndex - 1;
            } else if (newestIndex == 0) {
                newestIndex = queue->maxLen - 1;
            }

            memcpy((HI_VOID*)((HI_CHAR*)node + i * queue->nodeSize), queue->node[newestIndex], queue->nodeSize);
            tmpBufferCnt++;
        }
    }

    *bufferCnt = tmpBufferCnt;
    queue->curLen = 0;
    queue->frontIdx = 0;
    queue->rearIdx = 0;
    HI_MUTEX_UNLOCK(queue->mutex);
}


HI_S32 VIDEOPROCESS_PushQueue(HI_HANDLE queueHdl, HI_VOID* node)
{
    if (queueHdl == 0 || node == HI_NULL) {
        MLOGE("queueHdl or node is HI_NULL !\n");
        return HI_EINVAL;
    }

    VIDEOPROCESS_Queue* queue = (VIDEOPROCESS_Queue*)queueHdl;
    HI_MUTEX_LOCK(queue->mutex);

    /* if full, then newest will replace the oldest */
    if (queue->curLen > queue->maxLen) {
        HI_MUTEX_UNLOCK(queue->mutex);
        MLOGE("out of maxlen!\n");
        return HI_EINVAL;
    } else if (queue->curLen < queue->maxLen) {
        memcpy(queue->node[queue->frontIdx], node, queue->nodeSize);
        queue->curLen++;
        queue->frontIdx = (queue->frontIdx + 1) % queue->maxLen;
    } else if (queue->curLen == queue->maxLen) {
        memcpy(queue->node[queue->rearIdx], node, queue->nodeSize);
        queue->rearIdx = (queue->rearIdx + 1) % queue->maxLen;
        queue->frontIdx = queue->rearIdx;
    }
    HI_MUTEX_UNLOCK(queue->mutex);
    return HI_SUCCESS;
}

HI_S32 VIDEOPROCESS_PopQueue(HI_HANDLE queueHdl, HI_VOID* node, HI_BOOL* popNode)
{
    if (queueHdl == 0 || node == HI_NULL || popNode == HI_NULL) {
        MLOGE("queueHdl or node or popNode is HI_NULL!\n");
        return HI_EINVAL;
    }

    VIDEOPROCESS_Queue* queue = (VIDEOPROCESS_Queue*)queueHdl;
    HI_MUTEX_LOCK(queue->mutex);

    /* only full can be pop one node */
    if (queue->curLen == 0) {
        *popNode = HI_FALSE;
        HI_MUTEX_UNLOCK(queue->mutex);
        MLOGW("queue is empity!\n");
        return HI_EEMPTY;
    } else if (queue->curLen != queue->maxLen) {
        *popNode = HI_FALSE;
        HI_MUTEX_UNLOCK(queue->mutex);
        return HI_SUCCESS;
    } else if (queue->curLen == queue->maxLen) {
        memcpy(node, queue->node[queue->rearIdx], queue->nodeSize);
        queue->rearIdx = (queue->rearIdx + 1) % queue->maxLen;
        queue->curLen--;
    }

    *popNode = HI_TRUE;
    HI_MUTEX_UNLOCK(queue->mutex);
    return HI_SUCCESS;
}


HI_S32 VIDEOPROCESS_ReadQueue(HI_HANDLE queueHdl, HI_U32 bufferCnt, HI_VOID* node, HI_BOOL* readNode)
{
    if (queueHdl == 0 || node == HI_NULL || readNode == HI_NULL) {
        MLOGE("queueHdl or node or popNode is HI_NULL!\n");
        return HI_EINVAL;
    }

    VIDEOPROCESS_Queue* queue = (VIDEOPROCESS_Queue*)queueHdl;
    if (bufferCnt <= 0 || bufferCnt > queue->maxLen) {
        MLOGE("bufferCnt is not leagal!\n");
        return HI_EINVAL;
    }

    HI_MUTEX_LOCK(queue->mutex);

    if (queue->curLen == 0) {
        HI_MUTEX_UNLOCK(queue->mutex);
        MLOGW("queue is empity!\n");
        return HI_EEMPTY;
    }

    /* if bufferCnt bigger then currentLen, return nothing */
    if (bufferCnt > queue->curLen) {
        *readNode = HI_FALSE;
        HI_MUTEX_UNLOCK(queue->mutex);
        return HI_SUCCESS;
    }

    HI_S32 newestIndex = queue->frontIdx;
    for (HI_S32 i = 0; i < bufferCnt; i++) {
        if (newestIndex > 0) {
            newestIndex = newestIndex - 1;
        } else if (newestIndex == 0) {
            newestIndex = queue->maxLen - 1;
        }

        memcpy((HI_VOID*)((HI_CHAR*)node + i * queue->nodeSize), queue->node[newestIndex], queue->nodeSize);
    }

    *readNode = HI_TRUE;
    HI_MUTEX_UNLOCK(queue->mutex);

    return HI_SUCCESS;
}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* End of #ifdef __cplusplus */
